package com.birdboot.core;

import com.birdboot.annotation.Controller;
import com.birdboot.annotation.RequestMapping;
import com.birdboot.controller.UserController;
import com.birdboot.http.HttpServletRequest;
import com.birdboot.http.HttpServletResponse;
import jdk.nashorn.internal.objects.annotations.Constructor;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URISyntaxException;

/**
 * 该类实际上是SpringMVC框架的一个核心类,用于与服务器(Tomcat)整合,接手处理请求的工作
 */
public class DispatcherServlet {
    private static DispatcherServlet instance = new DispatcherServlet();
    private static File baseDir;//类加载路径
    private static File staticDir;//类加载路径下的static目录

    static {
        try {
            baseDir = new File(
                    ClientHandler.class.getClassLoader().getResource(".").toURI()
            );
            staticDir = new File(baseDir, "static");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    private DispatcherServlet() {
    }

    public static DispatcherServlet getInstance() {
        return instance;
    }

    public void service(HttpServletRequest request, HttpServletResponse response) {
        //对于表单上传的抽象路径而言，由于附带参数，因此无法再作为请求判断了
//        String path = request.getUri();
        //requestURI属性在请求对象中就是保存抽象路径的请求路径部分，不含有参数
        String path = request.getRequestURI();
        System.out.println("抽象路径:" + path);

        //首先判断请求路径是否为请求业务
        /*
            path:/regUser

            V17新增内容:
            当我们得到本次请求路径path的值后，我们首先要查看是否为请求业务:
            1:扫描controller包下的所有类
              //类加载路径
              File baseDir = new File(
                DispatcherServlet.class.getClassLoader().getResource(".").toURI()
              );
              File dir = new File(baseDir,"com/birdboot/controller");

            2:查看哪些被注解@Controller标注的过的类(只有被该注解标注的类才认可为业务处理类)
            3:遍历这些类，并获取他们的所有方法，并查看哪些时业务方法
              只有被注解@RequestMapping标注的方法才是业务方法
            4:遍历业务方法时比对该方法上@RequestMapping中传递的参数值是否与本次请求
              路径path值一致?如果一致则说明本次请求就应当由该方法进行处理
              因此利用反射机制调用该方法进行处理。
              提示:反射调用后要记得return,避免执行后续处理静态资源的操作
            5:如果扫描了所有的Controller中所有的业务方法，均未找到与本次请求匹配的路径
              则说明本次请求并非处理业务，那么执行下面请求静态资源的操作

            对于包而言我们只扫描com.birdboot.controller包
         */
        try {
            File baseDir = new File(
               DispatcherServlet.class.getClassLoader().getResource(".").toURI()
            );
            File dir = new File(baseDir,"com/birdboot/controller");
            File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
            for(File sub : subs){
                String fileName = sub.getName();
                String className = fileName.replace(".class","");
                Class cls = Class.forName("com.birdboot.controller."+className);
                //判断该类是否被注解@Controller标注?
                if(cls.isAnnotationPresent(Controller.class)){
                    //获取所有方法
                    Method[] methods = cls.getDeclaredMethods();
                    for(Method method : methods){
                        //判断该方法是否被注解@RequestMapping标注?
                        if(method.isAnnotationPresent(RequestMapping.class)){
                            //获取@RequestMapping注解参数
                            RequestMapping rm = method.getAnnotation(RequestMapping.class);
                            String value = rm.value();
                            if(value.equals(path)){//如果该注解参数与本次请求路径一致,说明本次请求由该方法处理
                                Object obj = cls.newInstance();//实例化该Controller
                                method.invoke(obj,request,response);
                                return;
                            }
                        }
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        File file = new File(staticDir, path);
        if (file.isFile()) {//file表示的是一个实际存在的文件
            response.setStatusCode(200);
            response.setStatusReason("OK");
            response.addHeader("Server", "BirdServer");
            response.setContentFile(file);
        } else {
            response.setStatusCode(404);
            response.setStatusReason("NotFound");
            file = new File(staticDir, "404.html");
            response.addHeader("Server", "BirdServer");
            response.setContentFile(file);
        }

    }
}
